#include "WorldModel.h"
#include <assert.h>
#include "EntityBackground.h"
#include "EntityCloud.h"
#include "EntityBoss.h"
#include "EntityEnemy.h"
#include "EntityExplosion.h"
#include "UserInterface.h"
#include "Sound.h"
#include "AI.h"
#include "ProjectileManager.h"
#include "EnemyManager.h"
#include "ExplosionManager.h"

CWorldModel* CWorldModel::instance=NULL;

/*return a instance of CWorldModel*/
CWorldModel& CWorldModel::getInstance(void)
{
	if(instance==NULL)
	{
		instance=new CWorldModel();
	}
	return* instance;
}
CWorldModel::CWorldModel(void):exit(false), timeStartLevel(-1), numberOfPoint(0), gameOver(false),
			timeBeginDisplayYOUWIN(-1),timeDisplayYOUWIN(3000), timeBeginDisplayGAMEOVER(-1),
			timeDisplayGAMEOVER(3000)
{}

CWorldModel::~CWorldModel(void)
{
	for (vector<CWorldEntity*>::iterator p=vectorEntity.begin(); p<vectorEntity.end(); p++)
	{
		delete (*p);
	}
}

/*create the entity player*/
void CWorldModel::createPlayer(int startPositionX, int startPositionY, int health, int speed, int state, int collisionCost, bool active,
							   int indexAnimationUp, int indexAnimationLeft, int indexAnimationRight, int speedAnimation)
{
	CWorldEntity* newEntity = new CEntityPlayer(startPositionX, startPositionY, health, speed, state, collisionCost, active,
		indexAnimationUp, indexAnimationLeft, indexAnimationRight, speedAnimation);
	indexPlayer=(int)this->vectorEntity.size();
	this->vectorEntity.push_back(newEntity);
	
}

/*create a entity cloud*/
void CWorldModel::createCloud(int startPositionX, int startPositionY, int indexSprite, int speed,
		int timeBetweenCloud, bool active)
{
	CWorldEntity* newEntity = new CEntityCloud(startPositionX, startPositionY, indexSprite, 
		speed,timeBetweenCloud, active);
	this->vectorEntity.push_back(newEntity);
	
}

/*create the entity background*/
void CWorldModel::createBackground(int startPositionBackgroundX, int startPositionBackgroundY, 
								   int indexSprite1, int indexSprite2, int speed, bool active)
{
	CWorldEntity* newEntity= new CEntityBackground(startPositionBackgroundX, startPositionBackgroundY, indexSprite1, 
													indexSprite2, speed, active);
	this->vectorEntity.push_back(newEntity);
}

/*create the entity boss*/
void CWorldModel::createBoss(int indexProjectileLeft, int indexProjectileRight, int timeToSpawn)
{
	CEntityBoss* newEntity= new CEntityBoss(timeToSpawn, indexProjectileLeft, indexProjectileRight);
	this->indexBoss=(int)this->vectorEntity.size();
	this->vectorEntity.push_back(newEntity);
}

/*function that add an entity to the entity vector*/
void CWorldModel::addEntity(CWorldEntity* const entity)
{
	this->vectorEntity.push_back(entity);
}

/*function that load all the information about the AI from an xlm file*/
void CWorldModel::loadLevel(TiXmlHandle handle)
{
	TiXmlElement* xmlLevel=handle.FirstChildElement().Child(4).FirstChildElement().Element();
	//for each level
	while(xmlLevel)
	{
		//get tje infromation about the level
		int indexLevel;
		int numberOfWave;
		xmlLevel->QueryIntAttribute("numberOfWave",&numberOfWave);
		AI.createNewLevel(&indexLevel,numberOfWave);
		TiXmlElement* xmlWaves=xmlLevel->FirstChildElement();
		TiXmlElement* xmlWave=xmlWaves->FirstChildElement();
		//for every wave
		for(int i=0;i<numberOfWave;i++)
		{
			//get the infroamtion about the wave
			int numberOfEnemies;
			int typeOfEnemy;
			int timeToSpawn;
			int timeToLive;
			int timeBetweenEnemies;
			xmlWave->QueryIntAttribute("numberOfEnemies", &numberOfEnemies);
			xmlWave->QueryIntAttribute("typeOfEnemies", &typeOfEnemy);
			xmlWave->QueryIntAttribute("timeToSpawn", &timeToSpawn);
			xmlWave->QueryIntAttribute("timeToLive", &timeToLive);
			xmlWave->QueryIntAttribute("timeBetweenEnemies", &timeBetweenEnemies);
			AI.initialiseWave(i,indexLevel,numberOfEnemies,typeOfEnemy,timeToSpawn,timeToLive,timeBetweenEnemies);
			TiXmlElement* xmlWayPoint=xmlWave->FirstChildElement();
			//for every waypoint
			while(xmlWayPoint)
			{
				//get the infromation about the waypoint
				int x;
				int y;
				xmlWayPoint->QueryIntAttribute("x",&x);
				xmlWayPoint->QueryIntAttribute("y",&y);
				AI.addWayPoint(indexLevel,i,CVector2D((float)x,(float)y));
				xmlWayPoint=xmlWayPoint->NextSiblingElement();
			}
			xmlWave=xmlWave->NextSiblingElement();

		}
		//get the information about the boss of the level
		xmlWaves=xmlWaves->NextSiblingElement();
		int projectileTypeLeft;
		int projectileTypeRight;
		int timeToSpawn;
		xmlWaves->QueryIntAttribute("projectileTypeLeft",&projectileTypeLeft);
		xmlWaves->QueryIntAttribute("projectileTypeRight",&projectileTypeRight);
		xmlWaves->QueryIntAttribute("timeToSpawn",&timeToSpawn);
		createBoss(projectileTypeLeft,projectileTypeRight , timeToSpawn);
		xmlLevel=xmlLevel->NextSiblingElement();
	}
	
	//tell to the UI component the range of index of the enemies in order to display their health
	USERINT.setIndexFirstEnemy(ENEMYMANAGER.getIndexFirstEnemy());
	USERINT.setIndexLastEnemy(ENEMYMANAGER.getIndexLastEnemy());
}

/*reset the data of the level when the player is dead or when he finished the level*/
void CWorldModel::resetLevel()
{
	//reset the data of the boss
	CEntityBoss* boss=(CEntityBoss*)this->vectorEntity[this->indexBoss];
	boss->resetBoss();

	//reset the data of every enemy
	if(ENEMYMANAGER.getIndexFirstEnemy()!=-1)
	{
		for(int p=ENEMYMANAGER.getIndexFirstEnemy(); p<=ENEMYMANAGER.getIndexLastEnemy();p++)
		{
			CEntityEnemy* e=(CEntityEnemy*)this->vectorEntity[p];
			e->resetEnemy();
		}
	}
	//reset the data of the player
	this->numberOfPoint=0;
	CEntityPlayer* p=(CEntityPlayer*)this->vectorEntity[indexPlayer];
	p->resetPlayer();

	//reset the data of the projectiles and explosions
	for(int p=PROJECTILEMANAGER.getIndexFirstProjectile();p<=PROJECTILEMANAGER.getIndexLastProjectile();p++)
	{
		this->vectorEntity[p]->setActive(false);
	}
	for(int p=EXPLOSIONMANAGER.getIndexFirstExplosion();p<=EXPLOSIONMANAGER.getIndexLastExplosion();p++)
	{
		this->vectorEntity[p]->setActive(false);
	}
	//reset the data of the world model
	timeStartLevel=-1;
	timeBeginDisplayYOUWIN=-1;
	timeBeginDisplayGAMEOVER=-1;
	gameOver=false;
}

/*function that call the update of every active entity*/
void CWorldModel::update(void)
{
	if(timeStartLevel==-1)
	{
		timeStartLevel=HAPI->GetTime();
	}
	if(USERINT.scanCode(HK_ESCAPE))
	{
		exit=true;
	}
	
	//call the update of every entities
	for(vector<CWorldEntity*>::iterator p=vectorEntity.begin(); p<vectorEntity.end();p++)
	{
		if ((*p)->getActive())
		{
			(*p)->update();
		}
	}

	//if the player is spawning he is invicible
	if(this->vectorEntity[indexPlayer]->getState()==CEntityPlayer::eSpawn)
	{
		vectorEntity[indexPlayer]->setCurrentHealth(vectorEntity[indexPlayer]->getTotalHealth());
	}
}

/*function that call the render of every active entity*/
void CWorldModel::render(void)
{
	for(vector<CWorldEntity*>::iterator p=vectorEntity.begin(); p<vectorEntity.end();p++)
	{
		if ((*p)->getActive())
		{
			(*p)->render();
		}
	}
	if(!this->vectorEntity[indexBoss]->getActive())
	{
		if(timeBeginDisplayYOUWIN==-1)
		{
			timeBeginDisplayYOUWIN=HAPI->GetTime();
		}
		if((int)HAPI->GetTime()>=timeBeginDisplayYOUWIN+timeDisplayYOUWIN)
		{
			this->gameOver=true;
		}
		VIS.displayText("CONGRATULATIONS",ePolice1,150,200,true);
	}
	CEntityPlayer* player=(CEntityPlayer*) this->vectorEntity[indexPlayer];
	if(player->isDead())
	{
		if(timeBeginDisplayGAMEOVER==-1)
		{
			timeBeginDisplayGAMEOVER=HAPI->GetTime();
		}
		if((int)HAPI->GetTime()>=timeBeginDisplayGAMEOVER+timeDisplayGAMEOVER)
		{
			this->gameOver=true;
		}
		VIS.displayText("GAME OVER", ePolice1, VIS.getScreenWidth()/2-100, VIS.getScreenHeight()/2-30, true);
	}
}

int CWorldModel::getCurrentPosX(int index)const
{
	return this->vectorEntity[index]->getCurrentPosX();
}
int CWorldModel::getCurrentPosY(int index)const
{
	return this->vectorEntity[index]->getCurrentPosY();
}

int CWorldModel::getState(int index)const
{
	return this->vectorEntity[index]->getState();
}
int CWorldModel::getSprite(int index)const
{
	return this->vectorEntity[index]->getIndexSprite();
}
int CWorldModel::getFrame(int index)const
{
	return this->vectorEntity[index]->getIndexFrame();
}

int CWorldModel::getExit(void)const
{
	return this->exit;
}
int CWorldModel::getNumberOfLife(int index)const
{
	return this->vectorEntity[index]->getNumberOfLife();
}
int CWorldModel::getTotalHealth(int index)const
{
	return this->vectorEntity[index]->getTotalHealth();
}
int CWorldModel::getCurrentHealth(int index)const
{
	return this->vectorEntity[index]->getCurrentHealth();
}
bool CWorldModel::getActive(int index)const
{
	return this->vectorEntity[index]->getActive();
}
int CWorldModel::getEntityVectorSize(void)const
{
	return (int)this->vectorEntity.size();
}
int CWorldModel::getTimeStartLevel(void)const
{
	return this->timeStartLevel;
}
int CWorldModel::getNumberOfPoint(void)const
{
	return this->numberOfPoint;
}
bool CWorldModel::getGameOver(void)const
{
	return this->gameOver;
}

int CWorldModel::getIndexPlayer()const
{
	return this->indexPlayer;
}
CWorldEntity* CWorldModel::getEntityPnter(int indexEntity)const
{
	return this->vectorEntity[indexEntity];
}

/*function that test if otherEntity collide with an other entity. If there is a collision
it returns a pointer to the entity*/
CWorldEntity* CWorldModel::checkCollision(CWorldEntity* otherEntity)
{
	for(vector<CWorldEntity*>::iterator p=this->vectorEntity.begin(); p<this->vectorEntity.end();p++)
	{
		if((*p)->getActive())
		{
			if(otherEntity->isEnemyOf((*p)))
			{
				if(otherEntity->checkForCollision((*p)))
				{
					return (*p);
				}
			}
		}
	}
	return NULL;
}

void CWorldModel::setExit(bool exit)
{
	this->exit=exit;
}
void CWorldModel::Close(void)
{
	delete CWorldModel::instance;
	CWorldModel::instance=NULL;
}
void CWorldModel::setTimeStartLevel(int timeStartLevel)
{
	this->timeStartLevel=timeStartLevel;
}
void CWorldModel::setNumberOfPoint(int numberOfPoint)
{
	this->numberOfPoint=numberOfPoint;
}
void CWorldModel::setGameOver(bool gameOver)
{
	this->gameOver=gameOver;
}